package com.gitee.wsl.collections.stack

interface Stack<E>: Collection<E> {

    fun push(value: E)

    fun pop(): E = popOrNull() ?: throw NoSuchElementException()

    fun popOrNull(): E?

    fun peek(): E = peekOrNull() ?: throw NoSuchElementException()

    fun peekOrNull(): E?

    companion object

}

inline fun <T> Stack<T>.usePeek(block: (T?) -> Unit) = peekOrNull().let(block)

inline fun <T> Stack<T>.usePop(block: (T?) -> Unit) = popOrNull().let(block)


/**
 * Creates a new [StackImp] instance wrapping the given values.
 *
 * The first of the given [items] will be the top of the stack, and the last of
 * the given items will be the bottom.
 *
 * Example:
 * ```
 * val stack = stackOf(1, 2, 3)
 *
 * stack.pop() // 1
 * stack.pop() // 2
 * stack.pop() // 3
 * ```
 *
 * @param items Items that will back the newly created stack.
 *
 * @return A new `Stack` instance.
 *
 * @author Elizabeth Paige Harper - https://github.com/foxcapades
 * @since 0.1.0
 */
@Suppress("UNCHECKED_CAST")
fun <T> stackOf(vararg items: T): Stack<T> {
    if(items.isEmpty()) return StackImp()
    items.reverse()
    return StackImp(items as Array<Any?>)
}

/**
 * Creates a new [StackImp] instance wrapping the values provided by the given
 * iterable.
 *
 * The first item provided by the [items] iterable will be the top of the stack,
 * and the last item provided by the iterable will be the bottom.
 *
 * Example:
 * ```
 * val items = listOf(1, 2, 3)
 * val stack = stackOf(items)
 *
 * stack.pop() // 1
 * stack.pop() // 2
 * stack.pop() // 3
 * ```
 *
 * @param items Items that will back the newly created stack.
 *
 * @return A new `Stack` instance.
 *
 * @author Elizabeth Paige Harper - https://github.com/foxcapades
 * @since 0.1.0
 */
fun <T> stackOf(items: Iterable<T>): Stack<T> {
    val tmp: Array<Any?> = items.toList().toTypedArray()
    tmp.reverse()
    return StackImp(tmp)
}